/*
  检测视频的兴趣点 
  为后续的特征提取做好工作
*/
#include "../common/common.h"

#if (!defined _STIP_HEADER_) 
#define _STIP_HEADER_


namespace STIP
{
  static void convertImage(IplImage* InputImage, IplImage* OutputImage,int scale = 0 )
{
  if( InputImage == NULL || OutputImage == NULL ) return ;
  uchar* InputData = (uchar*)InputImage->imageData ;
  float* InputPoint = NULL ;
  uchar* OutputData = (uchar*)OutputImage->imageData ;
  uchar* OutputPoint = NULL ;
  
  for( int index_row = 0 ; index_row < InputImage->height ; ++ index_row )
    {
      InputPoint = (float*)InputData;
      OutputPoint = OutputData ;
      for( int index_col = 0 ; index_col < InputImage->width ; ++ index_col )
	{
	  *OutputPoint = (uchar)(*InputPoint + scale) ;
	  InputPoint += 1 ;
	  OutputPoint += 1 ;
	}
      InputData += InputImage->widthStep ;
      OutputData += OutputImage->widthStep ;
    }
}
  static void convertImageto8U(IplImage* InputImage, IplImage* OutputImage)
{
  float max = -10000000;
  float min = 10000000 ;
  if( InputImage == NULL || OutputImage == NULL ) return ;
  uchar* InputData = (uchar*)InputImage->imageData ;
  float* InputPoint = NULL ;
  uchar* OutputData = (uchar*)OutputImage->imageData ;
  uchar* OutputPoint = NULL ;
  
  for( int index_row = 0 ; index_row < InputImage->height ; ++ index_row )
    {
      InputPoint = (float*)InputData;
      OutputPoint = OutputData ;
      for( int index_col = 0 ; index_col < InputImage->width ; ++ index_col )
	{
	  if( *InputPoint > max ) max = *InputPoint ;
	  if( *InputPoint < min ) min = *InputPoint ;
	  InputPoint += 1 ;
	}
      InputData += InputImage->widthStep ;
    }
  InputData = (uchar*)InputImage->imageData ;
  for( int index_row = 0 ; index_row < InputImage->height ; ++ index_row )
    {
      InputPoint = (float*)InputData;
      OutputPoint = OutputData ;
      for( int index_col = 0 ; index_col < InputImage->width ; ++ index_col )
	{
	  *OutputPoint = (uchar)(*InputPoint-min)/(max-min) ;
	  InputPoint += 1 ;
	  OutputPoint += 1 ;
	}
      InputData += InputImage->widthStep ;
      OutputData += OutputImage->widthStep ;
    }
}
// 得到高斯卷积核 
static cv::Mat getGaussianKernel(int n, double sigma,int ktype) 
{
  const int SMALL_GAUSSIAN_SIZE = 7 ;
  static const float small_gaussian_tab[][SMALL_GAUSSIAN_SIZE]=
    {
      {1.f},
      {0.25f, 0.5f, 0.25f},
      {0.0625f, 0.25f, 0.375f, 0.25f, 0.0625f},
      {0.03125f, 0.109375f, 0.21875f, 0.28125f, 0.21875f, 0.109375f, 0.03125f}
    };
  
  const float * fixed_kernel = n % 2 == 1 && n <= SMALL_GAUSSIAN_SIZE && sigma <= 0 ? small_gaussian_tab[n>>1] : NULL ;
  cv::Mat kernel(n,1,ktype) ;
  float * cf = (float*)kernel.data ;
  double * cd = (double*)kernel.data ;
  
  double sigmaX = sigma > 0 ? sigma : ((n-1)*0.5-1)*0.3 + 0.8 ; 
  double scale2X = -0.5/(sigmaX*sigmaX) ;
  double sum = 0 ;
  
  for( int index= 0 ; index < n ; ++ index )
    {
      double x = index - (n-1)*0.5 ;
      double t = fixed_kernel ? (double)fixed_kernel[index]:std::exp(scale2X*x*x) ;
      if( ktype == CV_32F )
	{
	  cf[index] = (float) t ;
	  sum += cf[index] ;
	}
      else
	{
	  cd[index] = t ;
	  sum += cd[index] ;
	}
    }
  sum = 1./sum ;
  for( int index = 0 ; index < n ; ++ index )
    {
      if( ktype == CV_32F )
	cf[index] = (float)(cf[index]*sum) ;
      else cd[index] *= sum ;
    }
  return kernel ;
}
// 计算兴趣点
class Stip
{
 public:
  typedef std::list<std::pair<int, IplImage*> >::iterator listIterType ;

 public:
  Stip(double _sigmaSpace = 4, double _sigmaTime = 4, double _K = 0.005, int _neighbourSpace = 5, int _neighbourTime = 5, float _thresholdStrength = 100000) ;
  ~Stip();
  void calculateSTIP();
  bool getReady() ;
  void drawCircle(IplImage* imagePtr, std::vector<cv::Point>&_pointsList) ;
  void setVideoName(const char * _aviFileName, const char* _stipPointsFileName) ;
  // private:
  void init();
  void clear();
  void calculateSpaceGradient(IplImage*imagePtr);
  void calculateTimeGradient(std::list<std::pair<int,IplImage*> > &_gaussianTimeImageList) ;
  void calculateStrength() ;
  void calculatePoints(float _thresholdStrength = -1, int width = 5, int height = 5) ; // 
  void calculatePointsMy(float maxL, float aerf) ;
  void gaussianGradient() ;
  void gaussianSmooth(IplImage*,int,cv::Mat) ; // 空间上的高斯平滑
  void gaussianSmoothTimeList(std::list<std::pair<int,IplImage*> > &_imageList,int _neighbourTime, float _sigmaTime) ; // 时间上的高斯平滑
  void gaussianSmoothGradientTimeList(listIterType &listIter, int _neighbourTime, float _sigmaTime) ;
  void outputPoint(std::ofstream &_outFileStream,std::vector<cv::Point>&_pointsList,int _frameNums) ; // 将计算的每帧图像的关键角点输出
  int indexRange(int,int) ;
  // private:

  std::list<std::pair<int,IplImage*> > imageList ;// 存储视频序列
  std::list<std::pair<int,IplImage*> > gaussianTimeImageList ; // time gaussian video sequence
  std::list<std::pair<int,IplImage*> >  gradientTimeImageList ; // 存储时间上的灰度
  IplImage* gradientXImage ; // 存储灰度x
  IplImage* gradientYImage ; // 存储灰度y
  
  IplImage* stipStrengthImage ; // 存储角点的强度值
  IplImage* gradientXXImage ; 
  IplImage* gradientYYImage ; 
  IplImage* gradientXYImage ; 
  IplImage* gradientXTimeImage ;
  IplImage* gradientYTimeImage ;
  IplImage* gradientTimeTimeImage ;
  IplImage* gradientTimeImage ;
  std::vector<cv::Point> pointsList ; 
  double sigmaSpace ; // 空间方差
  double sigmaTime ; // 时间方差
  double K ; // 角点值时的比例 k=0.04-0.06 opencv 给出0.05-0.5
  float thresholdStrength ; 
  int neighbourSpace ; // 空间领域窗口大小 
  int neighbourTime ; // time region
  int aperture ; // sobel 边缘检测算子的大小
  std::string aviFileName ;
  std::string stipPointsFileName ;
  std::ofstream outFileStream ;
  bool aviFileReady ;
};
}
#endif 
